home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 2 / Atari Mega Archive CD - Volume 2.iso / minix / up1510b.tgz / up1510b / src / commands / backup.c < prev    next >
C/C++ Source or Header  |  1990-07-23  |  12KB  |  505 lines

  1. /* backup - backup a directory        Author: Andy Tanenbaum */
  2.  
  3. /* This program recursively backs up a directory.  It has two typical uses:
  4.  *
  5.  * 1. Backing up a directory to 1 or more diskettes
  6.  * 2. Backing up RAM disk to a shadow directory on hard disk
  7.  *
  8.  * The backup directory or medium may be empty, in which case, the entire
  9.  * source directory is copied, or it may contain an old backup, in which
  10.  * case only those files that are new or out of date are copied.  In this
  11.  * respect, 'backup' resembles 'make', except that no 'makefile' is needed.
  12.  * The backed up copy may optionally be compressed to save space.
  13.  *
  14.  * The following flags exist:
  15.  *
  16.  *    -d  At the top level, only back up directories (not loose files)
  17.  *    -j  Don't copy junk: *.o, *.Z, *.bak, *.log, a.out, and core
  18.  *    -m  If ENOSPC encountered, ask for another diskette
  19.  *    -n  No directories, only loose files are backed up
  20.  *    -s  Don't copy *.s files
  21.  *      -t  set creation date of target-file equal to cdate of source-file
  22.  *    -v  Verbose (announce what is being done)
  23.  *    -z  Compress the backed up files
  24.  *
  25.  */
  26.  
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <errno.h>
  30. #include <stdio.h>
  31. #include <fcntl.h>
  32. #include <utime.h>
  33.  
  34. #define COPY_SIZE 4096
  35. #define MAX_ENTRIES 512
  36. #define DIR_ENT_SIZE 16
  37. #define NAME_SIZE 14
  38. #define MAX_PATH 256
  39. #define NONFATAL 0
  40. #define FATAL 1
  41. #define NO_SAVINGS 512        /* compress can return code 2 */
  42. #define OUT_OF_SPACE 2
  43.  
  44. typedef unsigned short unshort;
  45.  
  46. struct dir_buf {        /* list of the src directory */
  47.   unshort ino;
  48.   char name[NAME_SIZE];
  49. } dir_buf[MAX_ENTRIES];
  50.  
  51. struct sorted {
  52.   int mode;            /* file mode */
  53.   char *namep;            /* pointer to name in dir_buf */
  54.   long acctime;            /* time of last access */
  55.   long modtime;            /* time of last modification */
  56. } sorted[MAX_ENTRIES];
  57.  
  58. char copybuf[COPY_SIZE];
  59.  
  60. unshort dflag, jflag, mflag, nflag, rflag, sflag, tflag, vflag, zflag;
  61.  
  62. extern int errno;
  63. extern char *environ;
  64.  
  65. main(argc, argv)
  66. int argc;
  67. char *argv[];
  68. {
  69.   int i, ct, n, m, fd;
  70.   char *dir1, *dir2, *cp, c;
  71.  
  72.   /* Get the flags. */
  73.   sync();
  74.   if (argc < 3 || argc > 4) usage();
  75.   if (argc == 4) {
  76.     cp = argv[1];
  77.     if (*cp++ != '-') usage();
  78.     while ((c = *cp++) != '\0') {
  79.         switch (c) {
  80.             case 'd':    dflag++;    break;
  81.             case 'j':    jflag++;    break;
  82.             case 'm':    mflag++;    break;
  83.             case 'n':    nflag++;    break;
  84.             case 's':    sflag++;    break;
  85.             case 'r':    rflag++;    break;
  86.             case 't':    tflag++;    break;
  87.             case 'v':    vflag++;    break;
  88.             case 'z':    zflag++;    break;
  89.             default:    usage();
  90.         }
  91.     }
  92.     dir1 = argv[2];
  93.     dir2 = argv[3];
  94.   } else {
  95.     dir1 = argv[1];
  96.     dir2 = argv[2];
  97.   }
  98.  
  99.   /* Read in the source directory */
  100.  
  101.   fd = open(dir1, O_RDONLY);
  102.   if (fd < 0) error(FATAL, "cannot open ", dir1, "");
  103.   ct = read(fd, &dir_buf[0], MAX_ENTRIES * DIR_ENT_SIZE);
  104.   close(fd);
  105.   if (ct == MAX_ENTRIES * DIR_ENT_SIZE)
  106.     error(FATAL, "directory ", dir1, " is too large");
  107.  
  108.   /* Create the target directory. */
  109.   maketarget(dir2);
  110.  
  111.   /* Stat all the entries. */
  112.   n = ct / DIR_ENT_SIZE;
  113.   m = stat_all(dir1, n);
  114.  
  115.   /* Remove non-entries and sort what's left. */
  116.   sort_dir(m);
  117.  
  118.   /* Process each of the m entries one at a time. */
  119.   process(m, dir1, dir2);
  120.   exit(0);
  121. }
  122.  
  123.  
  124. maketarget(dir2)
  125. char *dir2;
  126. {
  127. /* The target directory is created if it does not already exist. */
  128.  
  129.   char *p, c, dbuf[MAX_PATH];
  130.  
  131.   if (access(dir2, 6) == 0)
  132.     return;            /* if target exists, we're done */
  133.   if (make_dir(dir2) == 0) return;    /* we just made it */
  134.  
  135.   /* We have to try creating all the higher level directories. */
  136.   strcpy(dbuf, dir2);
  137.   p = dbuf + 1;
  138.   while (1) {
  139.     while (*p != '/' && *p != '\0') p++;
  140.     c = *p;            /* either / or \0 */
  141.     *p = 0;
  142.     make_dir(dbuf);
  143.     if (c == '\0') return;
  144.     *p = c;
  145.     p++;
  146.   }
  147. }
  148.  
  149. int make_dir(dir)
  150. char *dir;
  151. {
  152. /* Create a directory. */
  153.   int pid, status;
  154.  
  155.   if ((pid = fork()) < 0)
  156.     error(FATAL, "cannot fork off mkdir to create ", dir, "");
  157.   if (pid > 0) {
  158.     /* Parent process waits for child (mkdir). */
  159.     wait(&status);
  160.     return(status);
  161.   } else {
  162.     /* Child process executes mkdir */
  163.     close(2);        /* don't want mkdir's error messages */
  164.     execle("/bin/mkdir", "mkdir", dir, (char *) 0, environ);
  165.     execle("/usr/bin/mkdir", "mkdir", dir, (char *) 0, environ);
  166.     error(FATAL, "cannot execute mkdir", "", "");
  167.   }
  168. }
  169.  
  170.  
  171. int stat_all(dir1, n)
  172. char *dir1;
  173. int n;
  174. {
  175. /* Stat all the directory entries.  By doing this all at once, the disk
  176.  * head can stay in the inode area.
  177.  */
  178.  
  179.   int i, j;
  180.   char cbuf[MAX_PATH];
  181.   struct stat s;
  182.   struct dir_buf *dp;
  183.   struct sorted *sp;
  184.  
  185.   for (i = 0; i < n; i++) {
  186.     /* Mark "." and ".." as null entries, as well as unstatable ones. */
  187.     if (strcmp(dir_buf[i].name, ".") == 0) dir_buf[i].ino = 0;
  188.     if (strcmp(dir_buf[i].name, "..") == 0) dir_buf[i].ino = 0;
  189.     if (dir_buf[i].ino == 0) continue;
  190.  
  191.     /* Stat the file. */
  192.     strcpy(cbuf, dir1);
  193.     strncat(cbuf, "/", 1);
  194.     strncat(cbuf, dir_buf[i].name, NAME_SIZE);
  195.     if (stat(cbuf, &s) < 0) {
  196.         error(NONFATAL, "cannot stat ", cbuf, "");
  197.         dir_buf[i].ino = 0;    /* mark as unusable */
  198.         continue;
  199.     }
  200.     sorted[i].mode = s.st_mode;
  201.     sorted[i].acctime = s.st_atime;
  202.     sorted[i].modtime = s.st_mtime;
  203.     sorted[i].namep = dir_buf[i].name;
  204.   }
  205.  
  206.   /* Squeeze out all the entries who ino field is 0. */
  207.   j = 0;
  208.   for (i = 0; i < n; i++) {
  209.     if (dir_buf[i].ino != 0) {
  210.         sorted[j] = sorted[i];
  211.         j++;
  212.     }
  213.   }
  214.   return(j);
  215. }
  216.  
  217.  
  218. int sort_dir(m)
  219. int m;
  220. {
  221. /* Sort the directory using bubble sort. */
  222.  
  223.   struct sorted *sp1, *sp2;
  224.  
  225.   for (sp1 = &sorted[0]; sp1 < &sorted[m - 1]; sp1++) {
  226.     for (sp2 = sp1 + 1; sp2 < &sorted[m]; sp2++) {
  227.         if (strncmp(sp1->namep, sp2->namep, NAME_SIZE) > 0)
  228.             swap(sp1, sp2);
  229.     }
  230.   }
  231. }
  232.  
  233.  
  234. process(m, dir1, dir2)
  235. int m;
  236. char *dir1, *dir2;
  237. {
  238. /* Process each entry in sorted[].  If it is a regular file, stat the target
  239.  * file.  The the source is newer, copy it.  If the entry is a directory,
  240.  * recursively call the entire program to process the directory.
  241.  */
  242.  
  243.   int er, fmode, res, namlen;
  244.   struct sorted *sp;
  245.   struct stat s;
  246.   char cbuf[MAX_PATH];
  247.  
  248.   for (sp = &sorted[0]; sp < &sorted[m]; sp++) {
  249.     fmode = sp->mode & S_IFMT;
  250.     if (fmode == S_IFREG) {
  251.         /* Regular file.  Construct target name and stat it. */
  252.         strcpy(cbuf, dir2);
  253.         strncat(cbuf, "/", 1);
  254.         strncat(cbuf, sp->namep, NAME_SIZE);
  255.         namlen = strlen(sp->namep);
  256.         if (sp->namep[namlen - 2] != '.' || sp->namep[namlen - 1] != 'Z')
  257.             if (zflag && (namlen <= (NAME_SIZE - 2)))
  258.                 strncat(cbuf, ".Z", 2);
  259.         er = stat(cbuf, &s);
  260.         if (er < 0 || sp->modtime > s.st_mtime) {
  261.             res = copy(dir1, sp, cbuf);
  262.         } else {
  263.             res = NONFATAL;
  264.         }
  265.  
  266.         /* Check status of the copy. */
  267.         if (res == OUT_OF_SPACE) {
  268.             printf("Out of space while copying to %s\n", cbuf);
  269.             /* We ran out of space copying a regular file. */
  270.             if (mflag == 0)
  271.                 error(FATAL,"Disk full. Backup aborted","","");
  272.  
  273.             /* If -m, ask for new diskette and continue. */
  274.             newdisk(dir2);
  275.             sp--;
  276.             continue;
  277.         }
  278.     } else if (fmode == S_IFDIR) {
  279.         /* Directory.  Execute this program recursively. */
  280.         copydir(dir1, dir2, sp->namep);
  281.     } else if (fmode == S_IFBLK || fmode == S_IFCHR) {
  282.         /* Special file. */
  283.         strncpy(cbuf, sp->namep, NAME_SIZE);
  284.         printf("%s is special file.  Not backed up.\n", cbuf);
  285.     }
  286.   }
  287. }
  288.  
  289.  
  290.  
  291.  
  292. swap(sp1, sp2)
  293. struct sorted *sp1, *sp2;
  294. {
  295. /* Swap two directory entries. */
  296.  
  297.   struct sorted d;
  298.  
  299.   d = *sp1;
  300.   *sp1 = *sp2;
  301.   *sp2 = d;
  302. }
  303.  
  304.  
  305. int copy(dir1, sp, cbuf2)
  306. struct sorted *sp;
  307. char *dir1, *cbuf2;
  308. {
  309. /* Copy a regular file. */
  310.  
  311.   int fd1, fd2, nr, nw, res, n, namlen;
  312.   char cbuf1[MAX_PATH], *p;
  313.  
  314.   /* If the -j or -s flags were given, suppress certain files. */
  315.   p = sp->namep;
  316.   n = strlen(p);
  317.   if (jflag) {
  318.     if (strcmp(p, "a.out") == 0) return(0);
  319.     if (strcmp(p, "core") == 0) return (0);
  320.     if (strcmp(p + n - 2, ".o") == 0) return (0);
  321.     if (strcmp(p + n - 2, ".Z") == 0) return (0);
  322.     if (strcmp(p + n - 4, ".bak") == 0) return (0);
  323.     if (strcmp(p + n - 4, ".log") == 0) return (0);
  324.   }
  325.   if (sflag) {
  326.     if (strcmp(p + n - 2, ".s") == 0) return(0);
  327.   }
  328.   res = 0;
  329.   if (dflag) return(0);        /* backup -d means only directories */
  330.   strcpy(cbuf1, dir1);
  331.   strncat(cbuf1, "/", 1);
  332.   strncat(cbuf1, sp->namep, NAME_SIZE);    /* cbuf1 = source file name */
  333.  
  334.   /* At this point, cbuf1 contains the source file name, cbuf2 the target